// -*- mode: go; tab-width: 2; -*-

package main

import (
	// "encoding/base64"
	"exec"
  "fmt"
	"http"
	"io"
	"io/ioutil"
	"json"
	"log"
	"os"
	"websocket"
)

var home_file_path = "home.html"
var home_data []byte = nil

func getHomeData() []byte {
	if home_data == nil {
		var err os.Error = nil
		home_data, err = ioutil.ReadFile(home_file_path)
		if err != nil {
			log.Exit("Error while reading file %s\n", home_file_path)
		}
	}
	return home_data
}


func handleRoot(writer http.ResponseWriter, req *http.Request) {
	log.Printf("Unhandled request %s %s\n", req.Method, req.RawURL)
	http.Error(writer, fmt.Sprintf("%s not found", req.RawURL), 404)
}

func handleHome(writer http.ResponseWriter, req *http.Request) {
	log.Printf("New request %s %s\n", req.Method, req.RawURL)
	writer.SetHeader("Content-Type", "text/html; charset=utf-8")
	io.WriteString(writer, string(getHomeData()))
	io.WriteString(writer,
		fmt.Sprintf("<input type=\"hidden\" value=\"%s\"></input>\n", "SHELL_ID"))
	io.WriteString(writer, "<script type=\"text/javascript\"> main() </script>")
}

func handleStatic(writer http.ResponseWriter, req *http.Request, file_path string, content_type string) {
	log.Printf("Serving static content: %s\n", req.RawURL)
	writer.SetHeader("Content-Type", content_type)
	data, err := ioutil.ReadFile(file_path)
	if err != nil {
		log.Printf("Error while reading file %s\n", file_path)
		return
	}
	writer.Write(data)
}

type jsonReply struct {
	Error string
	WaitMsg string
	Stdout string
	Stderr string
}

func handleShell(writer http.ResponseWriter, req *http.Request) {
	log.Printf("Shell %s %s\n", req.Method, req.RawURL)
	writer.SetHeader("Content-Type", "text/json; charset=utf-8")

	log.Printf("Content length: %d bytes\n", req.ContentLength)
	data := make([]byte, req.ContentLength)
	_, err := io.ReadFull(req.Body, data)
	if err != nil {
		log.Printf("Error while reading request content: %s\n", err)
		return
	}

	shell_req := string(data)
	log.Printf("Shell request: \"%s\"\n", shell_req)

	reply := jsonReply{};

	defer func() {
		data, err := json.MarshalForHTML(reply)
		if err != nil {
			log.Println("Error while marshalling reply: ", err)
		} else {
			log.Printf("Replying JSON content: \"%s\"", string(data))
			writer.Write(data)
		}
	}()

	argv := make([]string, 1)
	argv[0] = shell_req
	envv := make([]string, 0)
	dir := "."
	cmd, err := exec.Run(shell_req, argv, envv, dir, exec.DevNull, exec.Pipe, exec.Pipe)
	if err != nil {
		log.Println("Error: ", err)
		reply.Error = err.String()
		return
	}

	// Wait for the process to complete
	wait_msg, err := cmd.Wait(0)

	if err != nil {
		log.Println("Error: ", err)
		reply.Error = err.String()
		return
	}

	stdout_data, err := ioutil.ReadAll(cmd.Stdout)
	if err != nil {
		log.Println("Error: ", err)
		reply.Error = err.String()
		return
	}

	stderr_data, err := ioutil.ReadAll(cmd.Stderr)
	if err != nil {
		log.Println("Error: ", err)
		reply.Error = err.String()
		return
	}

	reply.WaitMsg = wait_msg.String()

	// stdout_base64 := make([]byte, base64.StdEncoding.EncodedLen(len(stdout_data)))
	// base64.StdEncoding.Encode(stdout_base64, stdout_data)
	reply.Stdout = string(stdout_data)

	// stderr_base64 := make([]byte, base64.StdEncoding.EncodedLen(len(stderr_data)))
	// base64.StdEncoding.Encode(stderr_base64, stderr_data)
	reply.Stderr = string(stderr_data)

	writer.Flush()
}


func wsEchoServer(ws *websocket.Conn) {
	io.Copy(ws, ws)
}


func main() {
	http.HandleFunc(
		"/code.js",
		func(writer http.ResponseWriter, req *http.Request) {
	    handleStatic(writer, req, "code.js", "text/javascript")
  })
	http.HandleFunc(
		"/style.css",
		func(writer http.ResponseWriter, req *http.Request) {
	    handleStatic(writer, req, "style.css", "text/css")
  })
	http.HandleFunc("/home/", handleHome)
	http.HandleFunc("/", handleRoot)
	http.HandleFunc("/shell/", handleShell)
	http.Handle("/wsecho", websocket.Handler(wsEchoServer))
	err := http.ListenAndServe(":2222", nil)
	if err != nil {
		log.Exit("Unable to start HTTP listener: ", err.String())
	}
}
